package svc

import (
	"context"
	"fmt"
	"github.com/zachary/tbox/internal/config"
	"github.com/zachary/tbox/internal/web/global"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"github.com/zeromicro/go-zero/rest/httpx"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

var sCtx *ServiceContext
var newLock, initLock sync.Once

type ServiceContext struct {
	Config config.Config
	Redis  *redis.Redis
	Db     *gorm.DB
	IsInit bool
}

func NewServiceContext(c config.Config) *ServiceContext {
	newLock.Do(func() {
		sCtx = &ServiceContext{
			Config: c,
		}
	})
	return sCtx
}

func GetServiceContext() *ServiceContext {
	if sCtx == nil {
		panic("ServiceContext not initialized!")
	}
	return sCtx
}

func Init(svr *http.Server) {
	initLock.Do(func() {
		initRedisIns()
		initDb()
		initOther()
		sCtx.IsInit = true
	})
}

func initRedisIns() {
	if len(sCtx.Config.Redis.Host) == 0 {
		return
	}
	logx.Infof("Initializing redis ...")
	// 设置redis慢请求阈值
	redis.SetSlowThreshold(time.Millisecond * 500)
	sCtx.Redis = redis.MustNewRedis(sCtx.Config.Redis)
	logx.Infof("Redis Initialized.")
}

func initDb() {
	dbConf := sCtx.Config.Db
	if len(dbConf.Host) == 0 {
		return
	}
	logx.Infof("Initializing db ...")
	//if len(dbConf.ParamStr) == 0 {
	//	dbConf.ParamStr = getParamStr(dbConf.ConnParams)
	//}
	dbUrl := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?%s",
		dbConf.Username,
		dbConf.Password,
		dbConf.Host,
		dbConf.Port,
		dbConf.Db,
		dbConf.ParamStr)
	logx.Infof("DSN: %s", dbUrl)
	dialector := mysql.New(mysql.Config{
		DSN:                       dbUrl, // data source name
		DefaultStringSize:         256,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度，MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式，MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列，MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	})

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer（日志输出的目标，前缀和日志包含的内容——译者注）
		logger.Config{
			SlowThreshold:             time.Duration(dbConf.SlowThresholdMillisecond) * time.Millisecond, // 慢 SQL 阈值
			LogLevel:                  logger.Info,                                                       // 日志级别
			IgnoreRecordNotFoundError: true,                                                              // 忽略ErrRecordNotFound（记录未找到）错误
			Colorful:                  true,                                                              // 启用彩色打印
		},
	)

	option := &gorm.Config{
		//禁用默认全局事务
		SkipDefaultTransaction: true,
		//开启预编译sql
		PrepareStmt: true,
		Logger:      newLogger,
	}
	db, err := gorm.Open(dialector, option)
	if err != nil {
		logx.Must(err)
	}
	sqlDb, err := db.DB()
	if err != nil {
		logx.Must(err)
	}
	sqlDb.SetMaxOpenConns(dbConf.MaxOpenConns)
	sqlDb.SetMaxIdleConns(dbConf.MaxIdleConns)
	sqlDb.SetConnMaxIdleTime(time.Second * time.Duration(dbConf.ConnMaxIdleTime))
	sqlDb.SetConnMaxLifetime(time.Second * time.Duration(dbConf.ConnMaxLifetime))
	sCtx.Db = db
	logx.Infof("%+v", sqlDb.Stats())
	logx.Infof("DB Initialized.")
}

func initOther() {
	httpx.SetErrorHandler(global.ErrorHandlerCallback)
	httpx.SetErrorHandlerCtx(func(ctx context.Context, err error) (int, any) {
		return global.ErrorHandlerCallback(err)
	})
}
